package com.whynot.frame.basic.file;

import com.whynot.frame.basic.util.DateUtils;
import com.whynot.frame.basic.util.FileUtil;
import io.minio.MinioClient;
import io.minio.errors.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

import java.io.InputStream;

@Component
public class MinioUtil {

    private static final Logger logger = LoggerFactory.getLogger(MinioUtil.class);

    private static MinioUtil minioUtil;

    private MinioClient minioClient;

    @Value("${minio.url:}")
    private String MINIO_SERVER;

    @Value("${minio.accessKey:}")
    private String MINIO_ACCESS_KEY;

    @Value("${minio.secretKey:}")
    private String MINIO_SECRET_KEY;

    @Value("${minio.retryNum:3}")
    private int RETRY_NUM;

    public MinioClient getMinioClient() throws Exception {
        if(minioClient == null){
            minioClient = new MinioClient(MINIO_SERVER, MINIO_ACCESS_KEY, MINIO_SECRET_KEY, false);
        }
        return minioClient;
    }

    public void setMinioClient(MinioClient minioClient) {
        this.minioClient = minioClient;
    }

    public Boolean createBucket(String bucketName) throws Exception{
        if (!getMinioClient().bucketExists(bucketName))
            getMinioClient().makeBucket(bucketName);
        return true;
    }

    /**
     * 通用上传按月分桶
     * @param inputStream
     * @return
     * @throws Exception
     */
    public String uploadStream(InputStream inputStream, String fileName) throws Exception{
        String bucketName = DateUtils.getCurrentDateAsString("yyyyMM");
        String minioFilePath = FileUtil.newFileName(fileName);
        String mediaType = FileUtil.getExtension(fileName,"");
        return uploadStream(bucketName, minioFilePath, inputStream, mediaType);
    }

    /**
     * 从minio获取文件流
     * @param bucketName
     * @param minioFilePath
     * @return
     * @throws Exception
     */
    public InputStream getFileStream(String bucketName, String minioFilePath) throws Exception {
        return getMinioClient().getObject(bucketName, minioFilePath);
    }

    /**
     * 自定义上传文件至minio
     * @param bucketName
     * @param minioFilePath
     * @param inputStream
     * @param mediaType
     * @return
     * @throws Exception
     */
    public String uploadStream(String bucketName, String minioFilePath, InputStream inputStream, String mediaType) throws Exception {
        logger.info("uploadStream for bucketName={} minioFilePath={} inputStream.getclass={}, mediaType={}", bucketName,
                minioFilePath, inputStream.getClass(), mediaType);
        if (org.apache.commons.lang3.StringUtils.isBlank(mediaType)) {
            mediaType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
        }
        if(createBucket(bucketName)) {
            putObjectWithRetry(bucketName, minioFilePath, inputStream, mediaType);
        }
        return getMinioClient().getObjectUrl(bucketName, minioFilePath);
    }

    public void putObjectWithRetry(String bucketName, String objectName, InputStream stream, String contentType) throws Exception{
        int current = 0;
        boolean isSuccess = false;
        while (!isSuccess && current < RETRY_NUM) {
            try {
                getMinioClient().putObject(bucketName, objectName, stream, contentType);
                isSuccess = true;
            } catch (ErrorResponseException e) {
                logger.warn("[minio] putObject stream, ErrorResponseException occur for time =" + current, e);
                current++;
            }
        }
        if (current == RETRY_NUM) {
            logger.error("[minio] putObject, backetName={}, objectName={}, failed finally!");
        }
    }

    public void putObjectWithRetry(String bucketName, String objectName, String fileName, String contentType) throws Exception {
        int current = 0;
        boolean isSuccess = false;
        while (!isSuccess && current < RETRY_NUM) {
            try {
                getMinioClient().putObject(bucketName, objectName, fileName, contentType);
                isSuccess = true;
            } catch (ErrorResponseException e) {
                current++;
                logger.debug("[minio] putObject file, ErrorResponseException occur!", e);
            }
        }
        if (current == RETRY_NUM) {
            logger.error("[minio] putObject, backetName={}, objectName={}, failed finally!");
        }
    }
}
